import { Callout } from '@/components'

# v3로 마이그레이션하기

## 새로운 기능

### 새로운 `mutationOptions` [#1490](https://github.com/toss/suspensive/pull/1490)

`mutationOptions`는 Mutation에서 옵션 객체를 쉽게 재사용하고 일관성 있게 관리할 수 있도록 도와줍니다. 이는 [`queryOptions`](/docs/react-query/queryOptions)가 사용되는 이유와 유사한 장점을 제공합니다.

```jsx /mutationOptions/
import { mutationOptions, useMutation, Mutation } from '@suspensive/react-query'


const editPostMutationOptions = (postId: number) =>
  mutationOptions({
    mutationFn: (content: string) => fetch(`https://example.com/posts/${postId}`, {
      method: 'PATCH',
      body: JSON.stringify({ content }),
    }).then(res => res.json()),
  })

// 커스텀 Mutation 훅을 만들 필요 없이 mutationOptions를 직접 활용할 수 있습니다.
const editPostMutation = useMutation(editPostMutationOptions(1))

// <Mutation />에서 직접 mutationOptions를 활용할 수 있습니다.
const Example = () => (
  <Mutation {...editPostMutationOptions(1)}>
    {({ mutate, isLoading }) => (
      <div>
        <p>{isLoading ? 'Updating...' : 'Latest updated'}</p>
        <textarea onBlur={(e) => mutate(e.target.value)} disabled={isLoading} />
      </div>
    )}
  </Mutation>
)
```

## BREAKING CHANGES 처리하기

### `@suspensive/react-query`의 `useSuspenseQuery`, `useSuspenseQueries`, `useSuspenseInfiniteQuery`에서 `networkMode`를 `'always'`로 고정하고 수정할 수 없습니다. [#1458](https://github.com/toss/suspensive/pull/1458)

<Callout type='info'>

이 브레이킹 체인지는 `@tanstack/react-query` v4와 `@suspensive/react-query`를 함께 사용하는 사용자에게 영향을 미칩니다.

</Callout>

기존에는 `navigator.onLine` API를 통해 온라인 상태를 판단했으나, 크로미움 기반 브라우저에서 실제 네트워크 상태와 다르게 판단되는 버그가 있었습니다. 브라우저 환경에 따라 동작이 일관되지 않다는 점을 고려하여, `@suspensive/react-query` v4에서는 Suspense 훅 사용 시 `networkMode`의 기본값을 `'always'`로 설정했습니다.

이는 React Query v5의 기본 동작과도 일치하며, 런타임 환경에서 Suspense 훅이 `fetchStatus: 'paused'`로 시작하는 문제를 방지합니다.

- 참고: [Default networkMode configuration and fetchStatus = "paused"](https://github.com/TanStack/query/discussions/4753), [navigator.onLine returning false value even network is available.](https://issues.chromium.org/issues/338514113)

```diff
import { queryOptions, useSuspenseQuery } from '@suspensive/react-query'

// queryOptions, infiniteQueryOptions에도 동일하게 적용됐습니다.
const QueryOptionsExample = () => {
  const options = queryOptions({
    queryKey: ['key'],
    queryFn: () => api()
-   networkMode: 'always'
  })
}

// useSuspenseQuery, useSuspenseInfiniteQuery, SuspenseQuery, SuspenseInfiniteQuery에도 동일하게 적용됐습니다.
const SuspenseQueryExample = () => {
  const query = useSuspenseQuery({
    queryKey: ['key'],
    queryFn: () => api()
-   networkMode: 'always'
  })
}
```

### `<QueryErrorBoundary/>`를 제거합니다. [#1424](https://github.com/toss/suspensive/pull/1424)

@suspensive/react-query v2에서는 @suspensive/react를 peerDependency로 활용해 QueryErrorBoundary를 제공하고 있었습니다.
하지만 이 방식은 간단한 구현을 위해서 사용자의 의존성을 복잡하게 할 수 있다는 이유로 삭제하고 단순화합니다.
따라서 이제 @suspensive/react는 더 이상 @suspensive/react-query의 peerDependency가 아니게 되었고 의존성 관계가 단순해졌습니다.

아래와 같이 QueryErrorBoundary를 직접 구현해서 사용할 수 있습니다.

```diff
- import { QueryErrorBoundary } from '@suspensive/react-query'
+ import { ErrorBoundary } from '@suspensive/react'
+ import { useQueryErrorResetBoundary } from '@tanstack/react-query'
+ import {
+   type ComponentPropsWithoutRef,
+   type ComponentRef,
+   forwardRef,
+ } from 'react'

+ const QueryErrorBoundary = forwardRef<
+   ComponentRef<typeof ErrorBoundary>,
+   ComponentPropsWithoutRef<typeof ErrorBoundary>
+ >(({ onReset, ...props }, resetRef) => {
+   const { reset } = useQueryErrorResetBoundary()
+   return (
+     <ErrorBoundary
+       {...props}
+       onReset={() => {
+         onReset?.()
+         reset()
+       }}
+       ref={resetRef}
+     />
+   )
+ })

const Example = () => (
  <QueryErrorBoundary>
    <SuspenseQuery />
  </QueryErrorBoundary>
)
```
